---
title: "Agent Approval | Agents"
description: Learn how to require approvals and suspend tool execution while keeping humans in control of agent workflows.
---

# Agent Approval

Agents sometimes require the same [human-in-the-loop](/docs/v1/workflows/human-in-the-loop) oversight used in workflows when calling tools that handle sensitive operations, like deleting resources or performing running long processes. With agent approval you can suspend a tool call and provide feedback to the user, or approve or decline a tool call based on targeted application conditions.

## Tool call approval

Tool call approval can be enabled at the agent level and apply to every tool the agent uses, or at the tool level providing more granular control over individual tool calls.

### Storage

Agent approval uses a snapshot to capture the state of the request. Ensure you've enabled a storage provider in your main Mastra instance. If storage isn't enabled you'll see an error relating to snapshot not found.

```typescript title="src/mastra/index.ts"
import { Mastra } from "@mastra/core/mastra";
import { LibSQLStore } from "@mastra/libsql";

export const mastra = new Mastra({
  // ...
  storage: new LibSQLStore({
    id: "mastra-storage",
    url: ":memory:"
  })
});
```


## Agent-level approval

When calling an agent using `.stream()` set `requireToolApproval` to `true` which will prevent the agent from calling any of the tools defined in its configuration.

```typescript showLineNumbers
const stream = await agent.stream("What's the weather in London?", {
  requireToolApproval: true
});
```

### Approving tool calls

To approve a tool call, access `approveToolCall` from the `agent`, passing in the `runId` of the stream. This will let the agent know its now OK to call its tools.

```typescript showLineNumbers
const handleApproval = async () => {
  const approvedStream = await agent.approveToolCall({ runId: stream.runId });

  for await (const chunk of approvedStream.textStream) {
    process.stdout.write(chunk);
  }
  process.stdout.write("\n");
};
```

### Declining tool calls

To decline a tool call, access the `declineToolCall` from the `agent`. You will see the streamed response from the agent, but it won't call its tools.

```typescript showLineNumbers
const handleDecline = async () => {
  const declinedStream = await agent.declineToolCall({ runId: stream.runId });

  for await (const chunk of declinedStream.textStream) {
    process.stdout.write(chunk);
  }
  process.stdout.write("\n");
};
```

## Tool-level approval

There are two types of tool call approval. The first uses `requireApproval`, which is a property on the tool definition, while `requireToolApproval` is a parameter passed to `agent.stream()`. The second uses `suspend` and lets the agent provide context or confirmation prompts so the user can decide whether the tool call should continue.

### Tool approval using `requireToolApproval`

In this approach, `requireApproval` is configured on the tool definition (shown below) rather than on the agent.

```typescript
export const testTool = createTool({
  id: "test-tool",
  description: "Fetches weather for a location",
  inputSchema: z.object({
    location: z.string()
  }),
  outputSchema: z.object({
    weather: z.string()
  }),
  resumeSchema: z.object({
    approved: z.boolean()
  }),
  execute: async ({ location }) => {
    const response = await fetch(`https://wttr.in/${location}?format=3`);
    const weather = await response.text();

    return { weather };
  },
  requireApproval: true
});
```

When `requireApproval` is true for a tool, the stream will include chunks of type `tool-call-approval` to indicate that the call is paused. To continue the call, invoke `resumeStream` with the required `resumeSchema` and the `runId`.

```typescript
const stream = await agent.stream("What's the weather in London?");

for await (const chunk of stream.fullStream) {
  if (chunk.type === "tool-call-approval") {
    console.log("Approval required.");
  }
}

const handleResume = async () => {
  const resumedStream = await agent.resumeStream({ approved: true }, { runId: stream.runId });

  for await (const chunk of resumedStream.textStream) {
    process.stdout.write(chunk);
  }
  process.stdout.write("\n");
};
```


### Tool approval using `suspend`

With this approach, neither the agent nor the tool uses `requireApproval`. Instead, the tool implementation calls `suspend` to pause execution and return context or confirmation prompts to the user.

```typescript

export const testToolB = createTool({
  id: "test-tool-b",
  description: "Fetches weather for a location",
  inputSchema: z.object({
    location: z.string()
  }),
  outputSchema: z.object({
    weather: z.string()
  }),
  resumeSchema: z.object({
    approved: z.boolean()
  }),
  suspendSchema: z.object({
    reason: z.string()
  }),
  execute: async ({ location }, { agent } = {}) => {
    const { resumeData: { approved } = {}, suspend } = agent ?? {};

    if (!approved) {
      return suspend?.({ reason: "Approval required." });
    }

    const response = await fetch(`https://wttr.in/${location}?format=3`);
    const weather = await response.text();

    return { weather };
  }
});
```

With this approach the stream will include a `tool-call-suspended` chunk, and the `suspendPayload` will contain the `reason` defined by the tool's `suspendSchema`. To continue the call, invoke `resumeStream` with the required `resumeSchema` and the `runId`.

```typescript
const stream = await agent.stream("What's the weather in London?");

for await (const chunk of stream.fullStream) {
  if (chunk.type === "tool-call-suspended") {
    console.log(chunk.payload.suspendPayload);
  }
}

const handleResume = async () => {
  const resumedStream = await agent.resumeStream({ approved: true }, { runId: stream.runId });

  for await (const chunk of resumedStream.textStream) {
    process.stdout.write(chunk);
  }
  process.stdout.write("\n");
};

```

## Related

- [Using Tools](./using-tools)
- [Agent Overview](./overview)
- [Tools Overview](../mcp/overview)
- [Agent Memory](./agent-memory)
- [Request Context](/docs/v1/server-db/request-context)
